﻿namespace Skymate.Mvc.Controllers.Dynamic.Builders
{
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Web.Http.Filters;

    using Skymate.Engines;
    using Skymate.Engines.DependencyManagement;
    using Skymate.Extensions;

    /// <summary>
    /// This interface is used to define a dynamic api controllers.
    /// </summary>
    /// <typeparam name="T">Type of the proxied object</typeparam>
    internal class BatchApiControllerBuilder<T> : IBatchApiControllerBuilder<T>
    {
        private readonly string _servicePrefix;
        private readonly Assembly _assembly;
        private IFilter[] _filters;
        private Func<Type, string> _serviceNameSelector;
        private Func<Type, bool> _typePredicate;
        private bool _conventionalVerbs;

        public BatchApiControllerBuilder(Assembly assembly, string servicePrefix)
        {
            this._assembly = assembly;
            this._servicePrefix = servicePrefix;
        }

        public IBatchApiControllerBuilder<T> Where(Func<Type, bool> predicate)
        {
            this._typePredicate = predicate;
            return this;
        }

        public IBatchApiControllerBuilder<T> WithFilters(params IFilter[] filters)
        {
            this._filters = filters;
            return this;
        }

        public IBatchApiControllerBuilder<T> WithServiceName(Func<Type, string> serviceNameSelector)
        {
            this._serviceNameSelector = serviceNameSelector;
            return this;
        }

        public IBatchApiControllerBuilder<T> WithConventionalVerbs()
        {
            this._conventionalVerbs = true;
            return this;
        }

        public void Build()
        {
            var types =
                from
                    type in this._assembly.GetTypes()
                where
                    type.IsPublic && type.IsInterface && typeof(T).IsAssignableFrom(type) && IocManager.Instance.IsRegistered(type)
                select
                    type;

            if (this._typePredicate != null)
            {
                types = types.Where(t => this._typePredicate(t));
            }

            foreach (var type in types)
            {
                var serviceName = this._serviceNameSelector != null
                    ? this._serviceNameSelector(type)
                    : GetConventionalServiceName(type);

                if (!string.IsNullOrWhiteSpace(this._servicePrefix))
                {
                    serviceName = this._servicePrefix + "/" + serviceName;
                }

                var builder = typeof(DynamicApiControllerBuilder)
                    .GetMethod("For", BindingFlags.Public | BindingFlags.Static)
                    .MakeGenericMethod(type)
                    .Invoke(null, new object[] { serviceName });

                if (this._filters != null)
                {
                    builder.GetType()
                        .GetMethod("WithFilters", BindingFlags.Public | BindingFlags.Instance)
                        .Invoke(builder, new object[] { this._filters });
                }

                if (this._conventionalVerbs)
                {
                    builder.GetType()
                       .GetMethod("WithConventionalVerbs", BindingFlags.Public | BindingFlags.Instance)
                       .Invoke(builder, new object[0]);
                }

                builder.GetType()
                        .GetMethod("Build", BindingFlags.Public | BindingFlags.Instance)
                        .Invoke(builder, new object[0]);
            }
        }

        public static string GetConventionalServiceName(Type type)
        {
            var typeName = type.Name;

            if (typeName.EndsWith("ApplicationServiceBase"))
            {
                typeName = typeName.Substring(0, typeName.Length - "ApplicationServiceBase".Length);
            }
            else if (typeName.EndsWith("AppService"))
            {
                typeName = typeName.Substring(0, typeName.Length - "AppService".Length);
            }
            else if (typeName.EndsWith("Service"))
            {
                typeName = typeName.Substring(0, typeName.Length - "Service".Length);
            }

            if (typeName.Length > 1 && typeName.StartsWith("I") && char.IsUpper(typeName, 1))
            {
                typeName = typeName.Substring(1);
            }

            return typeName.ToCamelCase();
        }
    }
}